﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CYTS.Aoyou.Framework.Config;
using CYTS.Aoyou.Framework.Utility;

namespace CYTS.Aoyou.Framework.Cache
{
    /// <summary>
    /// 缓存适配器工厂
    /// </summary>
    public class CacheAdapterFactory
    {
        private static object lockObj = new object();
        private static CacheAdapterFactory instance;
        private static IDictionary<string, ICacheAdapter> cacheAdapterList = new Dictionary<string, ICacheAdapter>(64);

        static CacheAdapterFactory()
        {
            ConfigFactory.OnModified += new Action(ConfigFactory_OnModified);
        }

        private CacheAdapterFactory()
        {

        }

        /// <summary>
        /// 获取对象服务缓存访问适配器工具实例
        /// </summary>
        /// <returns></returns>
        public static CacheAdapterFactory GetInstance()
        {
            if (instance == null)
            {
                lock (lockObj)
                {
                    if (instance == null)
                    {
                        instance = new CacheAdapterFactory();
                    }
                }
            }

            return instance;
        }

        /// <summary>
        /// 获取内存中所有的缓存适配器
        /// </summary>
        /// <returns></returns>
        public static IList<ICacheAdapter> GetAllCacheAdapterListInMemory()
        {
            return cacheAdapterList.Values.ToList();
        }

        /// <summary>
        /// 获取指定类型的缓存访问适配器(默认为Redis)
        /// </summary>
        /// <returns></returns>
        public T GetCacheAdapter<T>() where T : ICacheAdapter
        {
            CacheCategory cacheCategory = CacheCategory.Redis;
            CacheCategoryConfig config = ConfigFactory.GetConfig<CacheCategoryConfig>();
            Type type = typeof(T);
            string adapterName = type.Name.Substring(1, type.Name.Length - "CacheAdapter".Length - 1);
            if (config.CacheAdapters == null)
            {
                throw new Exception("配置文件关键元素缺失");
            }
            CacheAdapter adapterConfig = config.CacheAdapters.FirstOrDefault(p => p.Name == adapterName);
            if (adapterConfig != null)
            {
                cacheCategory = adapterConfig.CacheCategory;
            }
            return GetCacheAdapter<T>(cacheCategory);
        }

        /// <summary>
        /// 获取指定类型的缓存访问适配器
        /// </summary>
        /// <param name="cacheType">支持的缓存类型</param>
        /// <returns></returns>
        public T GetCacheAdapter<T>(CacheCategory cacheType) where T : ICacheAdapter
        {
            return (T)LoadCacheAdapter(typeof(T), cacheType);
        }

        private ICacheAdapter LoadCacheAdapter(Type adapterInterfaceType, CacheCategory cacheType)
        {
            ICacheAdapter adapter = this.PullAdapterFromCache(adapterInterfaceType, cacheType);
            if (adapter == null)
            {
                Type adapterType = GetAdapterType(adapterInterfaceType, cacheType);
                if (adapterType == null)
                {
                    throw new Exception("在 \"" + cacheType.ToString() + "\"找不到Adapter: \"" + adapterInterfaceType.FullName + "\" !");
                }

                adapter = (ICacheAdapter)Activator.CreateInstance(adapterType, GetCacheProviderByCacheType(cacheType));
                this.PushToAdapterCache(adapterInterfaceType, cacheType, adapter);
            }

            return (ICacheAdapter)adapter;
        }

        private Type GetAdapterType(Type cacheAdapterInterfaceType, CacheCategory cacheType)
        {
            string assemblyName = cacheAdapterInterfaceType.Namespace.Substring(0, cacheAdapterInterfaceType.Namespace.Length - "CAInterface".Length) + "CacheAccess";
            System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
            Type[] types = assembly.GetTypes();

            foreach (Type t in types)
            {
                if (TypeHelper.IsRealizeInterface(t, cacheAdapterInterfaceType) && TypeHelper.IsInheritBase(t, typeof(CacheAdapterBase)))
                {
                    if (t.Name.EndsWith(GetSuffixByCacheType(cacheType)))
                    {
                        return t;
                    }
                }
            }

            return null;
        }

        private void PushToAdapterCache(Type cacheAdapterInterfaceType, CacheCategory cacheType, ICacheAdapter adapter)
        {
            string key = cacheAdapterInterfaceType.FullName + "#" + cacheType.ToString();
            lock (lockObj)
            {
                if (cacheAdapterList.ContainsKey(key) == false)
                {
                    cacheAdapterList.Add(key, adapter);
                }
                else
                {
                    cacheAdapterList[key] = adapter;
                }
            }
        }

        private ICacheAdapter PullAdapterFromCache(Type cacheAdapterInterfaceType, CacheCategory cacheType)
        {
            string key = cacheAdapterInterfaceType.FullName + "#" + cacheType.ToString();
            ICacheAdapter adapter = null;
            cacheAdapterList.TryGetValue(key, out adapter);
            return adapter;
        }

        private string GetSuffixByCacheType(CacheCategory cacheType)
        {
            string suffix = string.Empty;
            switch (cacheType)
            {
                case CacheCategory.AppFabric:
                    suffix = "ForAppFabric";
                    break;
                case CacheCategory.Local:
                    suffix = "ForLocal";
                    break;
                case CacheCategory.Redis:
                    suffix = "ForRedis";
                    break;
                default:
                    suffix = cacheType.ToString();
                    break;
            }

            return suffix;
        }

        private CacheProvider GetCacheProviderByCacheType(CacheCategory cacheType)
        {
            var providerConfig = ConfigFactory.GetConfig<CacheProviderConfig>();
            if (providerConfig.CacheProviders == null)
            {
                throw new Exception("配置文件关键元素缺失");
            }
            return providerConfig.CacheProviders.First(p => p.Category == cacheType);
        }

        static void ConfigFactory_OnModified()
        {
            lock (lockObj)
            {
                cacheAdapterList.Clear();
            }
        }
    }
}
